package com.mask.im.server.service;

import org.springframework.data.redis.core.StringRedisTemplate;

import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 服务节点唯一标识管理类
 *
 * @author jian
 */
public class ServiceIdManager {
    // 本地存储文件路径
    private static final String LOCAL_ID_FILE = "./service-node-id.dat";
    // Redis存储键前缀
    private static final String REDIS_ID_KEY_PREFIX = "service:node:id:";
    // 节点信息在Redis中的过期时间（30秒）
    private static final long NODE_INFO_EXPIRE_SECONDS = 30;
    private final String serviceName;
    private final StringRedisTemplate redisTemplate;
    private final boolean useRedisStorage;
    private String nodeId;
    private String host;
    private int port;

    public ServiceIdManager(String serviceName, StringRedisTemplate redisTemplate,
                            boolean useRedisStorage, int port) {
        this.serviceName = serviceName;
        this.redisTemplate = redisTemplate;
        this.useRedisStorage = useRedisStorage;
        this.port = port;

        try {
            this.host = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            this.host = "unknown-host";
            System.err.println("获取主机IP失败: " + e.getMessage());
        }
        initNodeId();
        registerNodeToRedis();
    }

    /**
     * 初始化节点ID：优先从存储读取，不存在则生成新ID
     */
    private void initNodeId() {
        // 尝试从存储中读取
        String storedId = useRedisStorage ? readFromRedis() : readFromLocalFile();
        if (storedId != null && !storedId.isEmpty()) {
            this.nodeId = storedId;
            System.err.println("加载已存在的节点ID: " + nodeId);
        } else {
            // 生成新ID
            this.nodeId = generateNewNodeId();
            // 保存新ID
            boolean saved = useRedisStorage ? saveToRedis() : saveToLocalFile();
            if (!saved) {
                throw new RuntimeException("节点ID生成失败，无法完成初始化");
            }
            System.err.println("生成并保存新节点ID: " + nodeId);
        }
    }

    /**
     * 生成新的节点ID
     */
    private String generateNewNodeId() {
        // 格式: 服务名-IP-端口-UUID前8位
        String uuidPart = UUID.randomUUID().toString().substring(0, 8);
        return String.format("%s-%s-%d-%s", serviceName, host, port, uuidPart);
    }

    /**
     * 将节点信息注册到Redis，用于服务发现
     */
    public void registerNodeToRedis() {
        if (redisTemplate == null) {
            return;
        }
        String nodeInfoKey = "service:nodes:" + serviceName;
        // 将节点ID添加到服务节点集合
        redisTemplate.opsForSet().add(nodeInfoKey, nodeId);
        // 设置集合过期时间
        redisTemplate.expire(nodeInfoKey, NODE_INFO_EXPIRE_SECONDS, TimeUnit.SECONDS);

        // 存储节点详细信息（IP和端口）
        String nodeDetailKey = "service:node:detail:" + nodeId;
        String nodeDetail = String.format("%s:%d", host, port);
        redisTemplate.opsForValue().set(nodeDetailKey, nodeDetail);
        redisTemplate.expire(nodeDetailKey, NODE_INFO_EXPIRE_SECONDS, TimeUnit.SECONDS);
    }

    /**
     * 节点心跳续约
     */
    public void renewNodeLease() {
        if (redisTemplate == null) {
            return;
        }
        registerNodeToRedis(); // 续约本质上是重新注册
    }

    // 本地文件存储实现
    private String readFromLocalFile() {
        File file = new File(LOCAL_ID_FILE);
        if (!file.exists()) {
            return null;
        }

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            return reader.readLine();
        } catch (IOException e) {
            System.err.println("读取本地节点ID失败: " + e.getMessage());
            return null;
        }
    }

    private boolean saveToLocalFile() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(LOCAL_ID_FILE))) {
            writer.write(nodeId);
            return true;
        } catch (IOException e) {
            System.err.println("保存节点ID到本地文件失败: " + e.getMessage());
            return false;
        }
    }

    // Redis存储实现
    private String readFromRedis() {
        try {
            String key = REDIS_ID_KEY_PREFIX + serviceName + ":" + host + ":" + port;
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            System.err.println("从Redis读取节点ID失败: " + e.getMessage());
            return null;
        }
    }

    private boolean saveToRedis() {
        try {
            String key = REDIS_ID_KEY_PREFIX + serviceName + ":" + host + ":" + port;
            // 使用setIfAbsent确保原子性，防止并发问题
            return redisTemplate.opsForValue().setIfAbsent(key, nodeId, 365, TimeUnit.DAYS);
        } catch (Exception e) {
            System.err.println("保存节点ID到Redis失败: " + e.getMessage());
            return false;
        }
    }

    // Getters
    public String getNodeId() {
        return nodeId;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }
}
    